use std::{fs, sync::Arc};

use openssl::ssl::SslContext;
use serde::{Deserialize, Serialize};

use super::{
    backend::{HttpBackendConf, HttpBackendRuntimeConf},
    core_conf::{HttpCoreConf, HttpCoreRuntimeConf},
    h2_conf::{H2ServerConf, H2ServerRuntimeConf},
    main::get_http_main_runtime_conf,
    proxy_conf::{HttpProxyConf, HttpProxyRuntimeConf},
};

use crate::{
    config::ssl::{
        acceptor::{SslAcceptorConf, SslAcceptorRuntimeConf},
        connector::{SslConnectorConf, SslConnectorRuntimeConf},
    },
    error::*,
    openssl::connector::CustomConnectorBuilder,
};

#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct HttpAddressSiteConf {
    pub listen_on: Vec<String>,
    pub ssl_acceptor_conf: Option<SslAcceptorConf>,
    pub h2_server_conf: Option<H2ServerConf>,
    pub ssl_connector_conf: Option<SslConnectorConf>,
    pub core_conf: Option<HttpCoreConf>,
    pub proxy_conf: Option<HttpProxyConf>,
    pub backend_conf: Option<HttpBackendConf>,
}

#[allow(dead_code)]
impl HttpAddressSiteConf {
    pub fn to_runtime_conf(
        &self,
        server_name: String,
    ) -> Result<Arc<HttpAddressSiteRuntimeConf>> {
        let main_runtime_conf = get_http_main_runtime_conf();

        let ssl_acceptor_runtime_conf = match &self.ssl_acceptor_conf {
            Some(ssl_acceptor_conf) => ssl_acceptor_conf.to_runtime_conf(
                Some(&main_runtime_conf.ssl_acceptor_runtime_conf),
            )?,
            None => main_runtime_conf.ssl_acceptor_runtime_conf.clone(),
        };

        let h2_server_runtime_conf = match &self.h2_server_conf {
            Some(h2_server_conf) => h2_server_conf.to_runtime_conf(Some(
                &main_runtime_conf.h2_server_runtime_conf,
            ))?,
            None => main_runtime_conf.h2_server_runtime_conf.clone(),
        };

        let core_runtime_conf = match &self.core_conf {
            Some(core_conf) => core_conf
                .to_runtime_conf(Some(&main_runtime_conf.core_runtime_conf))?,
            None => main_runtime_conf.core_runtime_conf.clone(),
        };

        let proxy_runtime_conf = match &self.proxy_conf {
            Some(proxy_conf) => proxy_conf
                .to_runtime_conf(Some(&main_runtime_conf.proxy_runtime_conf))?,
            None => main_runtime_conf.proxy_runtime_conf.clone(),
        };

        let ssl_connector_runtime_conf = match &self.ssl_connector_conf {
            Some(ssl_connector_conf) => ssl_connector_conf.to_runtime_conf(
                Some(&main_runtime_conf.ssl_connector_runtime_conf),
            )?,
            None => main_runtime_conf.ssl_connector_runtime_conf.clone(),
        };

        let backend_runtime_conf = match &self.backend_conf {
            Some(backend_conf) => backend_conf.to_runtime_conf()?,
            None => Arc::new(HttpBackendRuntimeConf::default()),
        };

        let ssl_connector_ctx =
            CustomConnectorBuilder::new(ssl_connector_runtime_conf.clone())
                .build()
                .if_err(
                    ErrorType::ParseError,
                    "while in build ssl connector ctx",
                )?;

        let address_site_runtime_conf = HttpAddressSiteRuntimeConf {
            listen_on: self.listen_on.clone(),
            server_name,
            ssl_acceptor_runtime_conf,
            h2_server_runtime_conf,
            ssl_connector_runtime_conf,
            core_runtime_conf,
            proxy_runtime_conf,
            backend_runtime_conf,
            ssl_connector_ctx,
        };

        Ok(Arc::new(address_site_runtime_conf.valid()?))
    }
}

#[allow(dead_code)]
pub struct HttpAddressSiteRuntimeConf {
    pub listen_on: Vec<String>,
    pub server_name: String,
    pub ssl_acceptor_runtime_conf: Arc<SslAcceptorRuntimeConf>,
    pub h2_server_runtime_conf: Arc<H2ServerRuntimeConf>,
    pub ssl_connector_runtime_conf: Arc<SslConnectorRuntimeConf>,
    pub core_runtime_conf: Arc<HttpCoreRuntimeConf>,
    pub proxy_runtime_conf: Arc<HttpProxyRuntimeConf>,
    pub backend_runtime_conf: Arc<HttpBackendRuntimeConf>,

    // for ssl connector.
    pub ssl_connector_ctx: SslContext,
}

impl HttpAddressSiteRuntimeConf {
    fn valid(self) -> Result<Self> {
        Ok(self)
    }
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct HttpSiteConf {
    pub addr_conf_list: Vec<HttpAddressSiteConf>,
}

#[allow(dead_code)]
impl HttpSiteConf {
    pub fn load_from_yaml(
        path: &str,
        server_name: &str,
    ) -> Result<Arc<HttpSiteRuntimeConf>> {
        let conf_str = fs::read_to_string(path).if_err(
            ErrorType::ParseError,
            format!("Unable to read conf file from {path}"),
        )?;
        Self::from_yaml(&conf_str, server_name)
    }

    pub fn from_yaml(
        conf_str: &str,
        server_name: &str,
    ) -> Result<Arc<HttpSiteRuntimeConf>> {
        let conf: HttpSiteConf = serde_yaml::from_str(conf_str).if_err(
            ErrorType::ParseError,
            "Unable to parse http site conf conf",
        )?;
        conf.to_runtime_conf(server_name)
    }

    fn to_runtime_conf(
        &self,
        server_name: &str,
    ) -> Result<Arc<HttpSiteRuntimeConf>> {
        let mut address_runtime_conf_vec = Vec::new();

        for v in &self.addr_conf_list {
            let address_runtime_conf =
                v.to_runtime_conf(server_name.to_string())?;
            address_runtime_conf_vec.push(address_runtime_conf);
        }

        let site_runtime_conf = HttpSiteRuntimeConf {
            address_runtime_conf_vec,
        };

        Ok(Arc::new(site_runtime_conf.valid()?))
    }
}

#[allow(dead_code)]
pub struct HttpSiteRuntimeConf {
    pub address_runtime_conf_vec: Vec<Arc<HttpAddressSiteRuntimeConf>>,
}

#[allow(dead_code)]
impl HttpSiteRuntimeConf {
    fn valid(self) -> Result<Self> {
        Ok(self)
    }
}
